package main

import "fmt"

// func main() {
// 	Demo1()
// }

/* 闭包首先要确认的就是闭包的环境作用情况 */
func Demo1() {
	x := 133 // 基础变量环境

	// 封装闭包，这里会确定闭包的环境
	f1 := foo1(&x) // 表示闭包依赖环境指针
	f2 := foo2(x)  // 表示闭包依赖环境变量值
	/* 这里封装了两个闭包，也就配套了两份环境。区别只是f1使用指针，所以f1闭包的环境并不“封闭”，而f2的环境则是“封闭”的 */
	// 执行闭包
	f1() // 闭包执行过程中变量到底是什么情况只有执行状态才能知道 —— 并且，因为使用了指针，所以f1闭包执行过程会改变外部的基础环境的x（但是f2是封闭的，并不受影响）
	f2()
	f1()
	f2()

	// f1()
	// f2()
	// f2()
	// f2()
	// f1()
	// f2()

	fmt.Println("演示修改x的值：")
	/* 接下来，修改x */
	x = 233 // 修改了x，由于f1的闭包因链接了指针而“不封闭”，所以会受影响
	f1()
	f2() // 因为f2的封闭性，所以不受影响
	f1()
	f2()

	fmt.Println("演示封装闭包并执行")
	/* 下面是直接构筑了5个闭包 */
	foo1(&x)() // foo1 val = 236
	foo2(x)()  // foo2 val = 237
	foo1(&x)() // foo1 val = 237
	foo2(x)()  // foo2 val = 238
	foo2(x)()  // foo2 val = 238
}

func foo1(x *int) func() {
	return func() {
		*x = *x + 1
		fmt.Println("foo1 val = ", *x)
	}
}

func foo2(x int) func() {
	return func() {
		x = x + 1
		fmt.Println("foo2 val = ", x)
	}
}

// ------------------------------延迟绑定---------------------------------

func fooDe(x int) []func() {
	var fs []func()
	values := []int{1, 2, 3, 5}
	for _, val := range values {
		fs = append(fs, func() { // 此处只是声明了闭包
			fmt.Printf("fooDe val = %d\n", x+val)
		})
	}
	return fs
}

func Demo2() {

	f7s := fooDe(11) // 构造闭包，返回的也仅是一段闭包的函数定义——也就是得到代码
	/* 一般来说，构造闭包的时候，就会确定好闭包的环境，但是，这里其实没有办法得到确切的环境信息——因为也只是append了func */
	for _, f7 := range f7s {
		f7() // 执行闭包的时候，才知道闭包的环境具体是什么数值，内部变量才会进行赋值操作
		/* 所以，到了真正执行的时候，val已经由循环迭代到最后的值5了，闭包得到的环境信息也是5 */
	}
}

// ---------------------Go Runtime的延迟绑定-------------------------------
func foo5() {
	values := []int{1, 2, 3, 5}
	for _, val := range values {
		go func() { // 本质这段匿名函数的对象就是闭包
			// 只有执行的时候，内部的值才会得到具体的数值，并执行逻辑
			fmt.Printf("foo5 val = %v\n", val)
		}()
	}
	// 要是等到goroutine执行，那for-loop结束了呀，val生命周期早已结束了，程序应该报错才对呀？——所以也就是闭包延迟绑定特性
}

func Demo3() {
	foo5()
}
